#include <iostream>
#include <thread>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <cassert>
#include <cstdio>

//总结：互斥量--抢票系统；
//总体实现逻辑总结
// 1. 使用一个类，创建不同的线程；创建一个函数，执行抢票系统，抢票系统内部使用加锁和销毁锁的实现逻辑
// 2. 每次新线程执行的抢票的时候，都需要的检测是否还有余票，以及当前锁是否被其他线程拿走

using namespace std;

int tickets = 10000; // 临界资源(总票数)

#define THREAD_NUM 800

//创建新的线程
class ThreadData
{
public:
    ThreadData(const std::string &n,pthread_mutex_t *pm):tname(n), pmtx(pm)
    {}
public:
    std::string tname;
    pthread_mutex_t *pmtx;
};

//抢票函数实现（一次只可以一个执行流访问）
void *getTickets(void *args)
{
    // int myerrno = errno;
    ThreadData *td = (ThreadData*)args;
    while(true)
    {
        // 抢票逻辑
        int n = pthread_mutex_lock(td->pmtx);
        assert(n == 0);
        // 临界区
        if(tickets > 0) // 1. 判断的本质也是计算的一种
        {
            usleep(rand()%1500);
            printf("%s: %d\n", td->tname.c_str(), tickets);
            tickets--; // 2. 也可能出现问题
            n = pthread_mutex_unlock(td->pmtx);
            assert(n == 0);
        }
        else{
            n = pthread_mutex_unlock(td->pmtx);
            assert(n == 0);
            break;
        }
        
        // 抢完票，其实还需要后续的动作
        usleep(rand()%2000);
        //  errno = myerrno;
    }
    delete td;
    return nullptr;
}

int main()
{
    time_t start = time(nullptr);
    //多线程的创建和初始化
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, nullptr);

    srand((unsigned long)time(nullptr) ^ getpid() ^ 0x147);
    pthread_t t[THREAD_NUM];
    // 多线程抢票的逻辑（循环创建新的线程，并让其执行抢票的执行流任务）
    for(int i = 0; i < THREAD_NUM; i++)
    {
        std::string name = "thread ";
        name += std::to_string(i+1);

        ThreadData *td = new ThreadData(name, &mtx);
        pthread_create(t + i, nullptr, getTickets, (void*)td);
    }

    //等待创建的所有线程执行完对应的任务
    for(int i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(t[i], nullptr);
    }

    //循环销毁创建的线程
    pthread_mutex_destroy(&mtx);

    time_t end = time(nullptr);

    cout << "cast: " << (int)(end - start) << "S" << endl;
}
